<?php
// +----------------------------------------------------------------------
// | INPHP
// +----------------------------------------------------------------------
// | Copyright (c) 2021 https://inphp.cc All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/MIT )
// +----------------------------------------------------------------------
// | Author: lulanyin <me@lanyin.lu>
// +----------------------------------------------------------------------
namespace Inphp\Service;

use Inphp\Config;
use Swoole\Coroutine;
use Swoole\Process;

abstract class Server
{
    /**
     * 绑定IP
     * @var string
     */
    public string $ip = '0.0.0.0';

    /**
     * 端口
     * @var int
     */
    public int $port = 1990;

    /**
     * 服务对象
     * @var \Swoole\Http\Server|\Swoole\WebSocket\Server|null
     */
    public \Swoole\WebSocket\Server|\Swoole\Http\Server|null $server = null;

    /**
     * 服务类型
     * @var string
     */
    public string $server_type;

    /**
     * 配置
     * @var array
     */
    public array $settings = [
        //开启异步风格服务器的协程支持
        'enable_coroutine' => true,
        //不可超过CPU核心数 * 4，必须小于等于 worker_num
        'reactor_num'   => 4,
        //CPU的1~4倍为合理
        'worker_num'    => 4,
        //Task进程数量
        'task_worker_num' => 4,
        //Task开启协程
        'task_enable_coroutine' => true,
        //worker 进程的最大任务数，超过此任务数据，将会退出并释放所有资源
        'max_request'   => 100,
        //最小值为 (worker_num + task_worker_num) * 2 + 32
        'max_connection'=> 1000,
        //数据包分发策略
        'dispatch_mode' => 2,
        //守护进程
        'daemonize' => 0
    ];

    /**
     * 热更新进程
     * @var Process
     */
    public Process $hot_update_processor;
    
    public function __construct(bool $swoole = false)
    {
        //默认常规的PHP-FPM
        !defined("INPHP_SERVICE_PROVIDER") && define("INPHP_SERVICE_PROVIDER", $swoole ? Service::SWOOLE : Service::FPM);
        $config = Config::get("service.{$this->server_type}");
        $this->ip = $config['ip'];
        $this->port = $config['port'];
        $this->settings = array_merge($this->settings, $config['settings']);
        if($swoole){
            //协程Hook
            Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]);
            //使用 swoole\http\server
            $this->server = $this->server_type == Service::HTTP ? new \Swoole\Http\Server($this->ip, $this->port, SWOOLE_PROCESS, $config['sock_type'] ?? SWOOLE_SOCK_TCP) : new \Swoole\WebSocket\Server($this->ip, $this->port, SWOOLE_PROCESS, $config['sock_type'] ?? SWOOLE_SOCK_TCP);
            $this->server->set($this->settings);
            //各事件...
            $this->server->on('WorkerStart', [$this, 'onWorkerStart']);
            $this->server->on('start', [$this, 'onStart']);
            $this->server->on('task', [$this, 'onTask']);
            $this->server->on('finish', [$this, 'onFinish']);
            //如果启用了热更新，则运行一条进程
            if($config['hot_update']['enable']){
                $this->hot_update_processor = new Process([$this, 'hotUpdate']);
                $this->server->addProcess($this->hot_update_processor);
            }
        }
    }
    
    
    /**
     * 服务启动前
     *
     */
    public function beforeStart(){
        //中间键
        $this->processMiddlewares("before_start", [$this]);
    }

    /**
     * @param \Swoole\Http\Server|\Swoole\WebSocket\Server $server
     */
    public function onStart(\Swoole\Http\Server|\Swoole\WebSocket\Server $server){
        $ip = $this->ip == '0.0.0.0' ? '127.0.0.1' : $this->ip;
        echo "[".($this->server_type == Service::WS ? "websocket" : $this->server_type)."]服务已启动，地址是：{$this->server_type}://{$ip}:{$this->port}".PHP_EOL;
        $config = Config::get('service.'.$this->server_type);
        if($config['hot_update']['enable'] && $this->hot_update_processor){
            $this->hot_update_processor->write('start');
        }
        //中间键
        $this->processMiddlewares("on_start", [$server]);
    }

    /**
     * 每个进程启动
     * @param \Swoole\Http\Server|\Swoole\WebSocket\Server $server
     * @param int $worker_id
     */
    public function onWorkerStart(\Swoole\Http\Server|\Swoole\WebSocket\Server $server, int $worker_id = 0){
        //中间键
        $this->processMiddlewares("on_worker_start", [$server, $worker_id]);
    }

    /**
     * 异步任务投递
     * @param \Swoole\Http\Server|\Swoole\WebSocket\Server $server
     * @param \Swoole\Server\Task $task
     */
    public function onTask(\Swoole\Http\Server|\Swoole\WebSocket\Server $server, \Swoole\Server\Task $task){
        //中间键
        $this->processMiddlewares("on_task", [$server, $task]);
    }

    /**
     * 异步任务完成
     * @param \Swoole\Http\Server|\Swoole\WebSocket\Server $server
     * @param $task_id
     * @param mixed $data
     */
    public function onFinish(\Swoole\Http\Server|\Swoole\WebSocket\Server $server, $task_id, mixed $data){
        //中间键
        $this->processMiddlewares("on_finish", [$server, $task_id, $data]);
    }

    /**
     * 热更新
     * @param Process $process
     */
    public function hotUpdate(Process $process){
        while (true){
            Service::hotUpdate($this->server_type, function (){
                $this->reload();
            });
            //间隔
            $config = Config::get('service.'.$this->server_type);
            sleep($config['hot_update']['seconds'] ?? 10);
        }
    }

    /**
     * 统一处理中间键
     */
    public function processMiddlewares(string $name, array $args = []): void
    {
        $middleware_list = Config::get('service.'.$this->server_type.'.middleware.'.$name, []);
        $middleware_list = is_array($middleware_list) ? $middleware_list : [];
        Service::processMiddlewares($middleware_list, $args);
    }
    
    /**
     * 重载
     */
    public function reload(){
        $this->server?->reload();
    }

}